package com.tulink.yichutu.biz;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.tulink.yichutu.common.exception.BusinessException;
import com.tulink.yichutu.entity.*;
import com.tulink.yichutu.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 订单业务方法
 *
 * @author wanchaopeng
 */
@Slf4j
@Component
public class OrderBiz {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxVipOrderService wxVipOrderService;
    @Autowired
    private WxVipGoodsService wxVipGoodsService;
    @Autowired
    private WxUserService wxUserService;
    @Autowired
    private WxVipOrderPayLogService wxVipOrderPayLogService;
    @Autowired
    private WxVipDateLogService wxVipDateLogService;
    @Autowired
    private WxVipDateService wxVipDateService;
    @Autowired
    private CashCouponBiz cashCouponBiz;
    @Autowired
    private VipDateBiz vipDateBiz;
    @Autowired
    private GoodsBiz goodsBiz;

    public boolean antiBrush(String openId) {
        try {
            String antiBrushKey = getAntiBrushKey(openId);
            BoundValueOperations boundValueOperations = redisTemplate.boundValueOps(antiBrushKey);
            Long count = boundValueOperations.increment();
            boundValueOperations.expire(5, TimeUnit.MINUTES);
            if(count > 10) {
                return true;
            }
        } catch (Exception ex) {
            log.error("antiBrush error", ex);
        }
        return false;
    }

    public void removeAntiBrushKey(String openId) {
        try {
            String antiBrushKey = getAntiBrushKey(openId);
            redisTemplate.delete(antiBrushKey);
        } catch (Exception ex) {
            log.error("removeAntiBrushKey error", ex);
        }
    }

    public WxVipOrderEntity createOrder(String openId, String goodsId) {
        if(StringUtils.isBlank(openId) || StringUtils.isBlank(goodsId)) {
            return null;
        }
        WxVipOrderEntity orderEntity = checkAndCreateOrder(openId, goodsId);
        if(orderEntity == null) {
            return null;
        }
        orderEntity.setType(0);
        orderEntity.setStatus(0);
        wxVipOrderService.save(orderEntity);
        return orderEntity;
    }

    public void putOrderCache(WxVipOrderEntity orderEntity, LocalDateTime expireTime) {
        try {
            String orderKey = getOnWayOrderKey(orderEntity.getId());
            BoundValueOperations boundValueOperations = redisTemplate.boundValueOps(orderKey);
            boundValueOperations.set(JSONObject.toJSONString(orderEntity));
            boundValueOperations.expireAt(Date.from(expireTime.plusMinutes(1).atZone(ZoneId.systemDefault()).toInstant()));
        } catch (Exception ex) {
            log.error("putOrderCache error", ex);
        }
    }

    public WxVipOrderEntity getOrderCache(String orderId) {
        try {
            String orderKey = getOnWayOrderKey(orderId);
            Object val = redisTemplate.boundValueOps(orderKey).get();
            String json = val == null ? null : val.toString();
            if(!StringUtils.isBlank(json)) {
                return JSON.parseObject(json, WxVipOrderEntity.class);
            }
        } catch (Exception ex) {
            log.error("getOrder cache error", ex);
        }
        return null;
    }

    public WxVipOrderEntity getOrder(String orderId) {
        try {
            return wxVipOrderService.getById(orderId);
        } catch (Exception ex) {
            log.error("getOrder error", ex);
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    public String changeVipDate(String bizOrderId, String goodsId) {
        try {
            WxVipOrderEntity orderEntity = getOrder(bizOrderId);
            if(orderEntity == null) {
                return "订单不存在";
            }
            if(orderEntity.getStatus() != 1) {
                return "订单状态不支持";
            }
            WxVipGoodsEntity oldGoodsEntity = goodsBiz.getById(orderEntity.getGoodsId());
            if(oldGoodsEntity == null) {
                return "订单异常";
            }
            WxVipGoodsEntity newGoodsEntity = goodsBiz.getById(goodsId);
            if(newGoodsEntity == null) {
                return "商品不存在";
            }
            Date oldEndTime = vipDateBiz.getVipEndTime(orderEntity.getUserId());
            if(!vipDateBiz.isVip(oldEndTime)) {
                return "用户当前非会员，不能调整";
            }

            WxVipOrderEntity revertOrderEntity = new WxVipOrderEntity();
            revertOrderEntity.setUserId(orderEntity.getUserId());
            revertOrderEntity.setGoodsId(oldGoodsEntity.getId());
            revertOrderEntity.setGoodsCount(-1);
            revertOrderEntity.setGoodsPrice(oldGoodsEntity.getPrice());
            revertOrderEntity.setGoodsValue(oldGoodsEntity.getValue());
            revertOrderEntity.setType(2);
            revertOrderEntity.setStatus(1);
            revertOrderEntity.setPayTime(new Date());
            wxVipOrderService.save(revertOrderEntity);
            //保存处理日志
            saveDateLog(revertOrderEntity);
            if (!vipDateBiz.updateEndTime(
                    revertOrderEntity.getUserId(),
                    revertOrderEntity.getGoodsCount() * revertOrderEntity.getGoodsValue(),
                    false)) {
                throw new BusinessException("撤回订单失败");
            }

            WxVipOrderEntity newOderEntity = new WxVipOrderEntity();
            newOderEntity.setUserId(orderEntity.getUserId());
            newOderEntity.setGoodsId(newGoodsEntity.getId());
            newOderEntity.setGoodsCount(1);
            newOderEntity.setGoodsPrice(newGoodsEntity.getPrice());
            newOderEntity.setGoodsValue(newGoodsEntity.getValue());
            newOderEntity.setType(3);
            newOderEntity.setStatus(1);
            newOderEntity.setPayTime(new Date());
            wxVipOrderService.save(newOderEntity);
            //保存处理日志
            saveDateLog(newOderEntity);
            if (!vipDateBiz.updateEndTime(
                    newOderEntity.getUserId(),
                    newOderEntity.getGoodsCount() * newOderEntity.getGoodsValue(),
                    false)) {
                throw new BusinessException("充值失败");
            }

            Date endTime = vipDateBiz.getVipEndTime(orderEntity.getUserId());
            log.info("修改会员期限成功，{}, {}", orderEntity.getUserId(), VipDateBiz.getEndTimeFormat().format(endTime));
            return String.format("修改会员期限成功！\r\n" +
                            "修改后会员到期时间为：%s(修改前：%s)\r\n" +
                            "修改后会员：%s(修改前：%s)",
                    VipDateBiz.getEndTimeFormat().format(endTime),
                    VipDateBiz.getEndTimeFormat().format(oldEndTime),
                    newGoodsEntity.getName(),
                    oldGoodsEntity.getName()
            );
        } catch (BusinessException ex) {
            throw ex;
        } catch (Exception ex) {
            log.error("changeVipDate error, bizOrderId: {}, goodsId: {}", bizOrderId, goodsId);
            log.error("changeVipDate error", ex);
            throw new RuntimeException("修改会员期限处理失败");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public String deal(String userId, String openId, WxVipCashCouponEntity cashCouponEntity) {
        try {
            //生成订单
            WxVipOrderEntity orderEntity = checkAndCreateOrder(openId, cashCouponEntity.getGoodsId());
            if(orderEntity == null) {
                throw new BusinessException("兑换码错误");
            }
            orderEntity.setType(1);
            orderEntity.setStatus(1);
            orderEntity.setCashCode(cashCouponEntity.getCode());
            orderEntity.setPayTime(new Date());
            wxVipOrderService.save(orderEntity);

            //消费兑换码
            if(!cashCouponBiz.useCoupon(cashCouponEntity)) {
                throw new BusinessException("兑换码已被使用");
            }

            //保存会员延期处理日志
            saveDateLog(orderEntity);
            //增加会员时间
            if (!vipDateBiz.updateEndTime(orderEntity.getUserId(), orderEntity.getGoodsCount() * orderEntity.getGoodsValue())) {
                //乐观锁，重试一次
                if (!vipDateBiz.updateEndTime(orderEntity.getUserId(), orderEntity.getGoodsCount() * orderEntity.getGoodsValue())) {
                    throw new BusinessException("订单并发处理失败");
                }
            }
            removeAntiBrushKey(openId);

            Date endTime = vipDateBiz.getVipEndTime(userId);
            if(vipDateBiz.isLifetime(endTime)) {
                log.info("终身会员兑换成功，{}, {}", cashCouponEntity.getCode(), openId);
                return "兑换成功，尊贵的终身会员！\r\n\r\n提示：一个兑换码只能使用一次哦！";
            } else if(vipDateBiz.isVip(endTime)) {
                String endTimeDisplay = VipDateBiz.getEndTimeFormat().format(endTime);
                log.info("兑换成功，会员有效期为：{}，{}, {}", endTimeDisplay, cashCouponEntity.getCode(), openId);
                return "兑换成功，会员有效期为: " + endTimeDisplay + "\r\n\r\n提示：一个兑换码只能使用一次哦！";
            } else {
                throw new BusinessException("充值异常，请确认兑换码是否正常");
            }
        } catch (BusinessException ex) {
            throw ex;
        } catch (Exception ex) {
            log.error("deal error, openId: {}, cashCoupon: {}", openId, cashCouponEntity);
            log.error("deal error", ex);
            throw new RuntimeException("兑换处理失败");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void deal(WxVipOrderEntity orderEntity, WxPayOrderNotifyResult notifyResult) {
        try {
            //保存支付日志
            saveOrderPayLog(orderEntity.getId(), notifyResult);
            //更新状态为已成交
            wxVipOrderService.update(
                    Wrappers.<WxVipOrderEntity>lambdaUpdate()
                            .set(WxVipOrderEntity::getStatus, 1)
                            .set(WxVipOrderEntity::getPayTime, Date.from(LocalDateTime.parse(notifyResult.getTimeEnd(), DateTimeFormatter.ofPattern("yyyyMMddHHmmss")).atZone(ZoneId.systemDefault()).toInstant()))
                            .set(WxVipOrderEntity::getTransactionId, notifyResult.getTransactionId())
                            .eq(WxVipOrderEntity::getId, orderEntity.getId())
            );

            //保存会员延期处理日志
            saveDateLog(orderEntity);
            //增加会员时间
            if(!vipDateBiz.updateEndTime(orderEntity.getUserId(), orderEntity.getGoodsCount() * orderEntity.getGoodsValue())) {
                //乐观锁，重试一次
                if(!vipDateBiz.updateEndTime(orderEntity.getUserId(), orderEntity.getGoodsCount() * orderEntity.getGoodsValue())) {
                    throw new BusinessException("订单并发处理失败");
                }
            }
            removeAntiBrushKey(notifyResult.getOpenid());
        } catch (DuplicateKeyException | BusinessException ex) {
            throw ex;
        } catch (Exception ex) {
            log.error("deal error, order: {}, result: {}", orderEntity, notifyResult);
            log.error("deal error", ex);
            throw new RuntimeException("支付处理失败");
        }
    }

    private WxVipOrderEntity checkAndCreateOrder(String openId, String goodsId) {
        WxUserEntity userEntity = wxUserService.getOne(Wrappers.<WxUserEntity>lambdaQuery().eq(WxUserEntity::getOpenId, openId));
        if(userEntity== null) {
            log.warn("create order can not find openId: {}, goodsId: {}", openId, goodsId);
            return null;
        }
        if(userEntity.getSubscribe() != 1) {
            log.warn("create order user unsubscribe, openId: {}, goodsId: {}", openId, goodsId);
            return null;
        }
        WxVipGoodsEntity goodsEntity = goodsBiz.getById(goodsId);
        if(goodsEntity== null) {
            log.warn("create order can not find goodsId: {}, openId: {}", goodsId, openId);
            return null;
        }
        WxVipOrderEntity orderEntity = new WxVipOrderEntity();
        orderEntity.setUserId(userEntity.getId());
        orderEntity.setGoodsId(goodsId);
        orderEntity.setGoodsCount(1);
        orderEntity.setGoodsPrice(goodsEntity.getPrice());
        orderEntity.setGoodsValue(goodsEntity.getValue());
        return orderEntity;
    }

    private void saveDateLog(WxVipOrderEntity orderEntity) {
        WxVipDateLogEntity dateLogEntity = new WxVipDateLogEntity();
        dateLogEntity.setOrderId(orderEntity.getId());
        dateLogEntity.setUserId(orderEntity.getUserId());
        dateLogEntity.setValue(orderEntity.getGoodsValue() * orderEntity.getGoodsCount());
        wxVipDateLogService.save(dateLogEntity);
    }

    private void saveOrderPayLog(String orderId, Object data) {
        WxVipOrderPayLogEntity payLogEntity = new WxVipOrderPayLogEntity();
        payLogEntity.setOrderId(orderId);
        payLogEntity.setDetail(JSONObject.toJSONString(data));
        wxVipOrderPayLogService.save(payLogEntity);
    }

    private String getAntiBrushKey(String openId) {
        return String.format("order_anti_brush:%s", openId);
    }

    private String getOnWayOrderKey(String orderId) {
        return String.format("order:%s", orderId);
    }
}
